local utils = require "kong.tools.utils"
local singletons = require "kong.singletons"
local conf_loader = require "kong.conf_loader"
local cjson = require "cjson"
local api_helpers = require "kong.api.api_helpers"
local Schema = require "kong.db.schema"
local Errors = require "kong.db.errors"

local sub = string.sub
local kong = kong
local knode = (kong and kong.node) and kong.node or
        require "kong.pdk.node".new()
local errors = Errors.new()


local tagline = "Welcome to " .. _KONG._NAME
local version = _KONG._VERSION
local lua_version = jit and jit.version or _VERSION


local strip_foreign_schemas = function(fields)
    for _, field in ipairs(fields) do
        local fname = next(field)
        local fdata = field[fname]
        if fdata["type"] == "foreign" then
            fdata.schema = nil
        end
    end
end


return {
    ["/"] = {
        GET = function(self, dao, helpers)
            local distinct_plugins = setmetatable({}, cjson.array_mt)
            local prng_seeds = {}

            do
                local set = {}
                for row, err in kong.db.plugins:each() do
                    if err then
                        kong.log.err(err)
                        return kong.response.exit(500, { message = "An unexpected error happened" })
                    end

                    if not set[row.name] then
                        distinct_plugins[#distinct_plugins + 1] = row.name
                        set[row.name] = true
                    end
                end
            end

            do
                local kong_shm = ngx.shared.kong
                local shm_prefix = "pid: "
                local keys, err = kong_shm:get_keys()
                if not keys then
                    ngx.log(ngx.ERR, "could not get kong shm keys: ", err)
                else
                    for i = 1, #keys do
                        if sub(keys[i], 1, #shm_prefix) == shm_prefix then
                            prng_seeds[keys[i]], err = kong_shm:get(keys[i])
                            if err then
                                ngx.log(ngx.ERR, "could not get PRNG seed from kong shm")
                            end
                        end
                    end
                end
            end

            local node_id, err = knode.get_id()
            if node_id == nil then
                ngx.log(ngx.ERR, "could not get node id: ", err)
            end

            return kong.response.exit(200, {
                tagline = tagline,
                version = version,
                hostname = utils.get_hostname(),
                node_id = node_id,
                timers = {
                    running = ngx.timer.running_count(),
                    pending = ngx.timer.pending_count()
                },
                plugins = {
                    available_on_server = singletons.configuration.loaded_plugins,
                    enabled_in_cluster = distinct_plugins
                },
                lua_version = lua_version,
                configuration = conf_loader.remove_sensitive(singletons.configuration),
                prng_seeds = prng_seeds,
            })
        end
    },
    ["/endpoints"] = {
        GET = function(self, dao, helpers)
            local endpoints = setmetatable({}, cjson.array_mt)
            local lapis_endpoints = require("kong.api").ordered_routes

            for k, v in pairs(lapis_endpoints) do
                if type(k) == "string" then -- skip numeric indices
                    endpoints[#endpoints + 1] = k:gsub(":([^/:]+)", function(m)
                        return "{" .. m .. "}"
                    end)
                end
            end
            table.sort(endpoints, function(a, b)
                -- when sorting use lower-ascii char for "/" to enable segment based
                -- sorting, so not this:
                --   /a
                --   /ab
                --   /ab/a
                --   /a/z
                -- But this:
                --   /a
                --   /a/z
                --   /ab
                --   /ab/a
                return a:gsub("/", "\x00") < b:gsub("/", "\x00")
            end)

            return kong.response.exit(200, { data = endpoints })
        end
    },
    ["/schemas/:name"] = {
        GET = function(self, db, helpers)
            local entity = kong.db[self.params.name]
            local schema = entity and entity.schema or nil
            if not schema then
                return kong.response.exit(404, {
                    message = "No entity named '"
                            .. self.params.name .. "'"
                })
            end
            local copy = api_helpers.schema_to_jsonable(schema)
            strip_foreign_schemas(copy.fields)
            return kong.response.exit(200, copy)
        end
    },
    ["/schemas/:db_entity_name/validate"] = {
        POST = function(self, db, helpers)
            local db_entity_name = self.params.db_entity_name
            -- What happens when db_entity_name is a field name in the schema?
            self.params.db_entity_name = nil
            local entity = kong.db[db_entity_name]
            local schema = entity and entity.schema or nil
            if not schema then
                return kong.response.exit(404, {
                    message = "No entity named '"
                            .. db_entity_name .. "'"
                })
            end
            local schema = assert(Schema.new(schema))
            local _, err_t = schema:validate(schema:process_auto_fields(self.params, "insert"))
            if err_t then
                return kong.response.exit(400, errors:schema_violation(err_t))
            end
            return kong.response.exit(200, { message = "schema validation successful" })
        end
    },
    ["/schemas/plugins/:name"] = {
        GET = function(self, db, helpers)
            local subschema = kong.db.plugins.schema.subschemas[self.params.name]
            if not subschema then
                return kong.response.exit(404, {
                    message = "No plugin named '"
                            .. self.params.name .. "'"
                })
            end

            local copy = api_helpers.schema_to_jsonable(subschema)
            strip_foreign_schemas(copy.fields)
            return kong.response.exit(200, copy)
        end
    },
}
